
#include "artistic.h"
#include "common.h"
#include <GL/glu.h>
#include <iostream>
#include <QMouseEvent>
#include <QTimer>
#include <QFile>
#include <vector>
#include <cmath>
#include <assert.h>
#include <stdio.h>
#include <QMessageBox>
using namespace std;
//Constructor, camera gets initialized here

Artistic::Artistic(QWidget *parent) : QGLWidget(QGLFormat(QGL::SampleBuffers | QGL::DoubleBuffer),parent) , mFPS(30.0f) {

    //Test sphere
    mQuadric = gluNewQuadric();
    gluQuadricNormals(mQuadric, GL_SMOOTH);					// Generate Smooth Normals For The Quad
    gluQuadricTexture(mQuadric, GL_TRUE);						// Enable Texture Coords For The Quad

    mIncrement = 0;

    QObject::connect(this, SIGNAL(_mouseDrag(QMouseEvent*)), this, SLOT(mouseDragEvent(QMouseEvent*)));
    setAutoBufferSwap(false);
    setFocusPolicy(Qt::StrongFocus);
    mTimer = new QTimer(this);
    connect(mTimer, SIGNAL(timeout()), this, SLOT(redraw()));
    mTimer->start(1000.0f / mFPS);
    modeInteract = false;
    screenScale = 1.0;
//    drawer = new TreeDraw();
//    tree0 = new TreeGen2D();
    //scene = new Scene2D();
    scene = new Scene2D();
    scene->spaceColTree->m_widget = this;

    lambda = .58;
    tropism = .3;
    aux_angle = .75;
    shed_thres = .2;
    term_weight = 3;
    aux_weight = 3;
    diameter_rate = 2.8;
    tlength = 6.5;
    alength = 6.5;
    alpha = 2;
    shed_thres = .15;

    shadow_mode = true;
}

void Artistic::applyValues(){
    scene->tree0->LAMBDA = lambda;
    scene->tree0->TROPISM = tropism;
    scene->tree0->AUX_ANGLE = aux_angle;
    scene->tree0->SHED_THRES = shed_thres;
    scene->tree0->TERM_WEIGHT = term_weight;
    scene->tree0->AUX_WEIGHT = aux_weight;
    scene->tree0->DIAMETER_RATE = diameter_rate;
    scene->tree0->ALENGTH = alength;
    scene->tree0->TLENGTH = tlength;
    scene->tree0->ALPHA = alpha;

//    cout<<"LAMBDA "<<lambda<<endl;
//    cout<<"TROP "<<tropism<<endl;
//    cout<<"AUX_ANG "<<aux_angle<<endl;
//    cout<<"SHED THRES  "<<shed_thres<<endl;
//    cout<<"TERM W "<<term_weight<<endl;
//    cout<<"AUX W "<<aux_weight<<endl;
//    cout<<"DIAM R "<<diameter_rate<<endl;
//    cout<<"T LENGTH "<<alength<<endl;
//    cout<<"A LENGTH "<<tlength<<endl;
//    cout<<"ALPHA "<<alpha<<endl;

}

void Artistic::clearTree(){
    scene->deleteTree();
    applyValues();
}

void Artistic::iterateTree(){
    scene->iterate();
}


//destructor
Artistic::~Artistic() {
    SAFE_DELETE(mTimer);
    //SAFE_DELETE(mCamera);
    gluDeleteQuadric(mQuadric);
    makeCurrent();
//    delete drawer;
//    delete tree0;
    delete scene;
}

/**
    The main drawing method which will be called 30 frames per second.  Here
    is where your OpenGL drawing should occur.
**/
void Artistic::paintGL(){

      //some setup
    {
        float time = mIncrement++ / (float)mFPS;
        updateCamera();
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        glEnable(GL_CULL_FACE);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glMatrixMode(GL_MODELVIEW);
        glColor3f(1.0, 1.0, 1.0);
        glLoadIdentity();
        glBegin(GL_QUADS);
               glVertex3f(0.0f,0.0f,0.0);
        glVertex3f(CANVAS_SIZE,0.0f,0.0);
        glVertex3f(CANVAS_SIZE,CANVAS_SIZE,0.0);
         glVertex3f(0.0f,CANVAS_SIZE,0.0);
        glEnd();
        scene->draw();
        //drawer->drawTree(tree0);
    }

    swapBuffers();
    glFlush();

}

/**
  Setup OpenGL here.  You should specify your render settings within this method.
  This method is called once when Qt first retreives the OpenGL context during the
  creation of this widget.
  **/

void Artistic::initializeGL() {

     glEnable(GL_POLYGON_SMOOTH); //Enable smoothin
     glEnable(GL_COLOR_MATERIAL);
     glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); //set material properties which will be assigned by glColor
     glCullFace (GL_BACK);
     glDisable(GL_DEPTH_TEST);
     glEnable (GL_CULL_FACE);
     glShadeModel(GL_SMOOTH); //Smooth or flat shading model
     glPolygonMode(GL_FRONT, GL_FILL); //Shaded mode
     glPolygonMode(GL_BACK, GL_FILL);
     glClearColor(0.0,0.0, 0.0, 0.0); //Set the color to clear buffers to
     srand(3);
     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
         load_framebuffers(this->width(), this->height());



     updateCamera();

     scene->drawer->leaf = this->loadTexture("../cs224-self-organizing-trees/textures/leaf.png");
     scene->drawer->flower = this->loadTexture("../cs224-self-organizing-trees/textures/flower.png");


     QString frag = "../cs224-self-organizing-trees/224_Trees/color.frag";
     QString vert = "../cs224-self-organizing-trees/224_Trees/color.vert";

     scene->drawer->m_leaf = new QGLShaderProgram();
     scene->drawer->m_leaf->addShaderFromSourceFile(QGLShader::Fragment, frag);
     scene->drawer->m_leaf->addShaderFromSourceFile(QGLShader::Vertex, vert);
     bool b = scene->drawer->m_leaf->link();

    cout << "widget width:" << this->width() << endl;
     scene->initialize();

//     tree0->genTree();
}


/**
  Framebuffer methods
  **/
/***********************************************************************************************/
void Artistic::load_framebuffers(int w, int h){
    //Allocate the main framebuffer object for rendering the scene to
      //This needs a depth attachment.
    //Render buffer

       /*glGenRenderbuffersEXT(1, &framebuffer_objects["fbo_0"]);
       glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, framebuffer_objects["fbo_0"]);
       glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h);
       glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, framebuffer_objects["fbo_0"]);

       glGenFramebuffersEXT(1, &framebuffer_objects["fbo_1"]);
       glGenFramebuffersEXT(1, &framebuffer_objects["fbo_2"]);
       glGenFramebuffersEXT(1, &framebuffer_objects["fbo_3"]);*/
       /*Example framebuffer to texture code*/

       {
            /*
            //load some texture ID
            glGenTextures( 1, &textureID );
            glBindTexture( GL_TEXTURE_2D,textureID );

            glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB,
                             width(), height(),
                             0, GL_RGB, GL_UNSIGNED_BYTE, 0 );

            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
            //Then in paint GL
            glBindTexture( GL_TEXTURE_2D, mainSphereOffscreenFront);
            glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, framebuffer_objects["fbo_1"] );
            glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, mainSphereOffscreenFront, 0 );
            glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT,framebuffer_objects["fbo_0"] );
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
           //DRAW THINGS HERE
            //then unbind
            glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
            */
        }
  }


/**
  Texturing methods
  **/
/**********************************************************************************************************/
/*loads a texture with the name filename*/
GLuint Artistic::loadTexture(const QString &filename) {
    QImage image, texture;
    QFile file(filename);
    GLuint toReturn;
    if(!file.exists()){
        std::cout << "Failed to load texture: " << file.fileName().toStdString() << std::endl;
        return -1;
    }
    image.load(file.fileName());
   // image = image.mirrored(true, false);
    texture = QGLWidget::convertToGLFormat(image);

    glGenTextures(1, &toReturn);
    glBindTexture(GL_TEXTURE_2D, toReturn);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, texture.width(), texture.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
     //gluBuild2DMipmaps(GL_TEXTURE_2D, 3, texture.width(), texture.height(), GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    return toReturn;

}

/**
  User interaction, update methods...
  **/
/****************************************************************************************************************/

   /* Resets the camera to fit the new viewport when the user resizes
    the window.  (Avoids object distortion)*/
void Artistic::resizeGL(int w, int h) {
    // Prevent a divide by zero, when window is too short
    // (you cant make a window of zero width).

    if(h == 0) h = 1;
    // Set the viewport to be the entire window (you probably want to keep this)
    glViewport(0, 0, w, h);
        screenCenter.x = w/2;
        screenCenter.y = h/2;



    updateCamera();
}

void Artistic::updateCamera() {
    float w = width(), h = height();
    float ratio = 1.0f * w / h;
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    float width = static_cast<float>(w)*screenScale;
    float height = static_cast<float>(h)*screenScale;
    float leftX = screenCenter.x - width/2;
    float botY = screenCenter.y - height/2;

    glOrtho(leftX, width+leftX, botY, height+botY,-100.f,100.f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void Artistic::mousePressEvent(QMouseEvent *event) {
    if(!modeInteract){
        mLastMousePos.x = event->x(); mLastMousePos.y = event->y();
    }
    else{
        float xpos = (event->x()-width()/2)*screenScale+screenCenter.x;
        float ypos = (height()/2-event->y())*screenScale+screenCenter.y;
        scene->addMarkers(xpos, ypos );
    }
}

void Artistic::mouseMoveEvent(QMouseEvent *event) {
    if(event->buttons() & Qt::LeftButton || event->buttons() & Qt::RightButton) //Mouse drag - don't modify this
        emit _mouseDrag(event);
    else if (event->buttons() & Qt::NoButton) {
    }
    mLastMousePos.x = event->x(); mLastMousePos.y = event->y();
}
//NOTE: it resizes so that it has the last chance of being out of bounds
void Artistic::mouseDragEvent(QMouseEvent *event) {
    if(event->buttons() & Qt::LeftButton) {
        if (!modeInteract){
             float dx = event->x() - mLastMousePos.x;
             float dy = event->y() - mLastMousePos.y;
             screenCenter.x -=  dx*screenScale;
               screenCenter.y += dy*screenScale;
             fflush(stdout);
       }
        else{
        //perform interaction here
            float xpos = (event->x()-width()/2)*screenScale+screenCenter.x;
            float ypos = (height()/2-event->y())*screenScale+screenCenter.y;
            scene->addMarkers(xpos, ypos );
          // scene->addMarkers(event->x(),height() - event->y() );
        }
     }
}

void Artistic::wheelEvent(QWheelEvent *event) {
    screenScale +=  event->delta() * 1.0/1200.0;
    if (screenScale < 0.1) screenScale = 0.1;
             fflush(stdout);
}

void Artistic::keyPressEvent(QKeyEvent *event) {
    if(event->key() == Qt::Key_Q) {
        exit(0);
    }
}

void Artistic::redraw() {
    repaint();
}

bool Artistic::saveScene(QString filename)
{
    return scene->saveScene(filename);
}

bool Artistic::loadScene(QString filename)
{
    return scene->loadScene(filename);
}
